package utils

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"errors"
	"fmt"
	"github.com/RoaringBitmap/roaring/roaring64"
	"io"
	"math/rand"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"
)

var (
	defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
)

func Stack() string {
	var buf [2 << 10]byte
	return string(buf[:runtime.Stack(buf[:], true)])
}

func Map2Struct(m map[string]interface{}, tagName string, obj interface{}) {

	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	if t.Kind() != reflect.Ptr {
		return
	}

	t = t.Elem()
	if t.Kind() != reflect.Struct {
		return
	}

	v = v.Elem()

	var (
		data = make(map[string]interface{})
	)

	for i := 0; i < t.NumField(); i++ {
		var (
			fd = t.Field(i)
			vd = v.Field(i)
		)

		keyName := fd.Tag.Get(tagName)
		if keyName == "" || keyName == "-" {
			continue
		}
		if _, ok := m[keyName]; !ok {
			continue
		}
		if vd.CanSet() {
			vd.Set(reflect.ValueOf(m[keyName]))
		}

		data[keyName] = v.Field(i).Interface()
	}
	return
}

func Struct2Map(obj interface{}, tagName string) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		v = v.Elem()
	}

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		keyName := t.Field(i).Tag.Get(tagName)
		if keyName != "" && keyName != "-" {
			data[keyName] = v.Field(i).Interface()
		}
	}
	return data
}

func NowTime14BitWithDot() string {
	time13Bit := NowTime13Bit()
	return time13Bit[:10] + "." + time13Bit[10:13]
}

func NowTime13Bit() string {
	int64Time := time.Now().UnixNano() / 1e6
	return strconv.FormatInt(int64Time, 10)
}

func RandInt(min, max int) int {
	return min + rand.Intn(max-min)
}

func GzipDecode(input []byte) ([]byte, error) {
	// 创建一个新的 gzip.Reader
	bytesReader := bytes.NewReader(input)
	gzipReader, err := gzip.NewReader(bytesReader)
	defer func() {
		// defer 中关闭 gzipReader
		if gzipReader != nil {
			_ = gzipReader.Close()
		}
	}()
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	// 从 Reader 中读取出数据
	if _, err = buf.ReadFrom(gzipReader); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func RandomString(n int) string {
	var letters = defaultLetters

	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}

	return string(b)
}

// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息

	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}

	return true
}

// PathExist 判断目录是否存在
func PathExist(addr string) bool {
	s, err := os.Stat(addr)
	if err != nil {
		return false
	}
	return s.IsDir()
}

func ToFileFromBitMap(fileName string, ba *roaring64.Bitmap, args ...interface{}) error {

	var (
		bitArray     bool
		areaCode     string
		numberLength int
		extraLength  int
	)

	if len(args) == 1 {
		bitArray = args[0].(bool)
	}

	if len(args) == 2 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
	}

	if len(args) == 3 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
		numberLength = args[2].(int)
		extraLength = numberLength - len(areaCode)
	}

	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer f.Close()

	if bitArray {
		_, err = ba.WriteTo(f)
		if err != nil {
			return err
		}
		return nil
	}

	bufWriter := bufio.NewWriter(f)

	for _, v := range ba.ToArray() {

		vv := fmt.Sprintf("%0*d", extraLength, v)
		_, _ = bufWriter.WriteString(fmt.Sprintf("%v%v%c%c", areaCode, vv, 0x0D, 0x0A))
	}

	return bufWriter.Flush()
}

func NewBitMapFromFile(fileName string, args ...interface{}) (*roaring64.Bitmap, []string, error) {

	var (
		bitArray bool
		numbers  []string
		areaCode string // 暂时只对文本文件做过滤
	)

	if len(args) == 1 {
		bitArray = args[0].(bool)
	}

	if len(args) == 2 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
	}

	if b := PathExist(fileName); b {
		return nil, nil, errors.New("not file")
	}

	r := roaring64.New()
	f, err := os.Open(fileName)
	if err != nil {
		return nil, nil, err
	}

	defer f.Close()

	reader := bufio.NewReader(f)

	if bitArray {
		_, err = r.ReadFrom(f)
		if err != nil {
			return nil, nil, err
		}

		for _, v := range r.ToArray() {
			numbers = append(numbers, fmt.Sprintf("%v", v))
		}
	} else {
		for {
			line, _, err := reader.ReadLine()
			if err == io.EOF {
				break
			}

			/*
				1. 过滤前后空格
				2. 过滤非手机号
				3. 暂时不支持带 "-" 的料子，如果后面要支持，不能用 strconv.ParseUint 过滤
			*/

			lineStr := strings.TrimSpace(string(line))
			x, err := strconv.ParseUint(lineStr, 10, 64)
			if err != nil {
				continue
			}

			if strings.Index(lineStr, areaCode) != 0 {
				continue
			}

			numbers = append(numbers, fmt.Sprintf("%v", x))
			r.Add(x)
		}
	}

	return r, numbers, err
}

func ChunkSliceString(slice []string, chunkSize int) [][]string {
	numChunks := (len(slice) + chunkSize - 1) / chunkSize
	chunks := make([][]string, numChunks)

	for i := 0; i < numChunks; i++ {
		start := i * chunkSize
		end := start + chunkSize

		if end > len(slice) {
			end = len(slice)
		}

		chunks[i] = slice[start:end]
	}

	return chunks
}

func ChunkSliceInterface(slice []interface{}, chunkSize int) [][]interface{} {
	numChunks := (len(slice) + chunkSize - 1) / chunkSize
	chunks := make([][]interface{}, numChunks)

	for i := 0; i < numChunks; i++ {
		start := i * chunkSize
		end := start + chunkSize

		if end > len(slice) {
			end = len(slice)
		}

		chunks[i] = slice[start:end]
	}

	return chunks
}
